package user_service

import (
	"encoding/json"
	E "errors"
	"fmt"
	"math/rand"
	"os"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/redisop"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/errors"
	"peilian-api/app/global/log"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"peilian-api/app/services/wx_service"
	"peilian-api/utils/encrypt"
	"peilian-api/utils/sdk"
	"peilian-api/utils/tools"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

func SendCode(ctx *gin.Context, sc model.SendCodeRequest) error {
	act := sc.Action
	phone := sc.Phone
	// 1. 校验参数
	if act != "login" {
		return fmt.Errorf(errors.RequestParamsEmpty)
	}
	// 2. 发送短信
	if !tools.CheckMobile(phone) {
		return fmt.Errorf(errors.RequestParamsEmpty)
	}
	// 3. 发送短信
	if !sdk.SendSmsCode(phone) {
		return fmt.Errorf(errors.SendCodeError)
	}
	return nil
}

// 邮箱绑定发送验证码
func MailSendCode(ctx *gin.Context, scr model.MailSendCodeRequest) *errors.ErrRes {
	mail := scr.Mail
	// 发送邮箱
	code := fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(10000))
	redisCodeKey := fmt.Sprintf(common.RedisCodeKey, tools.Md5DataString(mail))
	sc := variable.DB.Redis.Set(redisCodeKey, code, 10*60*time.Second)
	if sc.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败, 请重新操作"))
	}
	if err := sdk.SendMailInfo(sdk.MsgTypeMailBindingCode, variable.Config.MailInfo.BusinessMail, sdk.MailInfo{MailTo: mail, Code: code}); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败, 请重新操作"))
	}
	return nil
}

// 邮箱找回密码-发送验证码
func MailSendCodeFind(ctx *gin.Context, scr model.MailSendCodeRequest) *errors.ErrRes {
	mail := scr.Mail
	// 根据mail查询该邮箱是否存在
	u, err := tables.GetUserInfoByUserEmail(mail, []string{tables.UserTypeAnonymous, tables.UserTypeNormal})
	if err != nil || u.ID == 0 {
		return errors.NewErrInfo(30001, fmt.Errorf("邮箱未绑定"))
	}
	if u.Sno == "" {
		return errors.NewErrInfo(30002, fmt.Errorf("邮箱未绑定"))
	}

	// 发送邮箱
	code := fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(10000))
	redisCodeKey := fmt.Sprintf(common.RedisCodeMailFindKey, tools.Md5DataString(mail))
	sc := variable.DB.Redis.Set(redisCodeKey, code, 10*60*time.Second)
	if sc.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败, 请重新操作"))
	}
	if err := sdk.SendMailInfo(sdk.MsgTypeMailFindCode, variable.Config.MailInfo.BusinessMail, sdk.MailInfo{MailTo: mail, Code: code}); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败, 请重新操作"))
	}
	return nil
}

func Login(ctx *gin.Context, lr model.LoginRequest, typ string, promoteId string) (out model.LoginResp, err error) {
	var user *tables.User
	var phone = "" // 定义变量
	var code = ""
	var school = ""
	var sno string = ""
	var password = ""

	var tokenExt = "" // token中扩展的字段

	if typ == "phone" {
		phone = lr.Ph.Phone
		code = lr.Ph.Code
		if phone == "" || code == "" {
			return out, fmt.Errorf(errors.RequestParamsEmpty)
		}
		if !tools.CheckMobile(phone) {
			return out, fmt.Errorf(errors.PhoneCheckError)
		}
	} else if typ == "wx" {
		code := lr.Wx.Code
		if code == "" {
			return out, fmt.Errorf(errors.RequestParamsEmpty)
		}
		// 根据code 获取phone
		wx := new(wx_service.WxService)
		phone, err = wx.GetUserPhoneNumberByCode(code)
		if err != nil {
			return out, fmt.Errorf("用户信息获取失败，请重试")
		}
		if !tools.CheckMobile(phone) {
			return out, fmt.Errorf(errors.PhoneCheckError)
		}
	} else if typ == "sc" {
		school = lr.Sc.School
		sno = lr.Sc.Sno
		password = lr.Sc.Password
	} else if typ == "ex" {
		clientId := lr.Ex.ClientId
		grantType := lr.Ex.GrantType

		if len(lr.Ex.Token) == 0 || len(grantType) == 0 || len(clientId) == 0 {
			return out, fmt.Errorf("您的登录信息错误")
		}
		isLogin := false
		encryptKey := encrypt.DefaultKey
		cids := map[string][]string{
			//variable.Config.Ex.Bd.ClientId:   {variable.Config.Ex.Bd.GrantType, encrypt.BdKey, variable.Config.Ex.Bd.AppSecret},
			//variable.Config.Ex.Bh.ClientId:   {variable.Config.Ex.Bh.GrantType, encrypt.BhKey, variable.Config.Ex.Bh.AppSecret},
			//variable.Config.Ex.Bl.ClientId:   {variable.Config.Ex.Bl.GrantType, encrypt.BlKey, variable.Config.Ex.Bl.AppSecret},
			//variable.Config.Ex.Rd.ClientId:   {variable.Config.Ex.Rd.GrantType, encrypt.RdKey, variable.Config.Ex.Rd.AppSecret},
			//variable.Config.Ex.Zpqy.ClientId: {variable.Config.Ex.Zpqy.GrantType, encrypt.ZpqyKey, variable.Config.Ex.Zpqy.AppSecret},
		}
		if s, ok := cids[clientId]; ok {
			if s[0] == grantType {
				isLogin = true
				encryptKey = s[1]
			}
		}
		if !isLogin {
			return out, fmt.Errorf("您无权登录")
		}
		// 解析token
		token, err := encrypt.RsaDecode(lr.Ex.Token, encryptKey)
		fmt.Printf("token:%s\nerr:%+v\n", token, err)
		tokenArr := strings.Split(token, "-")
		if len(tokenArr) < 6 {
			return out, fmt.Errorf("token解析失败(length:%d)，请重试", len(tokenArr))
		}
		dateTime, _ := strconv.ParseInt(tokenArr[3], 10, 64)
		if time.Now().Unix()-dateTime > 120 { // 两分钟过期
			return out, fmt.Errorf("您登录超时，请重试")
		}
		sign := tools.Md5DataString(fmt.Sprintf("%s-%s-%d", clientId, cids[clientId][2], dateTime))
		if sign != tokenArr[2] {
			return out, fmt.Errorf("您的token校验失败(sign)，请重试")
		}
		sno = tokenArr[4]
		school = tokenArr[5]
		// 北航将用户信息直接写到到了token 中 放到了第7位
		if len(tokenArr) >= 7 {
			tokenExt = tokenArr[6]
		}

	} else {
		return out, fmt.Errorf("登录信息错误")
	}

	if typ == "phone" {
		// 取redis中的验证码数据
		redisCode, err := variable.DB.Redis.Get(phone).Result()
		if err != nil {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
		// 验证码错误
		if redisCode != code {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
	} else if typ == "sc" {
		//os.Setenv("ENV", "local")
		if os.Getenv("ENV") != "local" {
			redisCode, err := variable.DB.Redis.Get(fmt.Sprintf(common.RedisCaptchaCodeKey, lr.Sc.Time, lr.Sc.Rand)).Result()
			if err != nil {
				return out, fmt.Errorf("验证码已过期,请重新获取")
			}
			if !strings.EqualFold(redisCode, lr.Sc.Code) {
				return out, fmt.Errorf("验证码已过期,请重新获取")
			}
		}
	}
	var isExist = true
	var tokenType = tools.TypePhone
	if typ == "phone" || typ == "wx" {
		// 查询该数据是否存在，还是第一次登录
		err2 := variable.DB.MySQL.Model(&user).Where("phone = ? and type in ('normal', 'anonymous')", phone).First(&user).Error
		if E.Is(err2, gorm.ErrRecordNotFound) {
			// 用户昵称
			timeUnix := time.Now().Unix() % 1670000000
			randCode := fmt.Sprintf("%03v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000))
			nameCode := fmt.Sprintf("用户%s%d", randCode, timeUnix)

			// 如果数据不存在，则需要补充数据
			user.Phone = phone
			user.Type = tables.UserTypeAnonymous
			user.Name = nameCode
			user.TrailCount = uint(GetDefaultUserTrailCount(ctx)) // 游客默认面试次数
			user.PromoteId = promoteId
			d := variable.DB.MySQL.Create(&user)
			if err := d.Error; err != nil {
				return out, fmt.Errorf(err.Error())
			}
			uid := uint(user.ID)
			user.ID = uid
			isExist = false
		}
	} else if typ == "sc" { // 学号登录
		err2 := variable.DB.MySQL.Model(&user).Where("school = ? and sno = ?", school, sno).First(&user).Error
		if E.Is(err2, gorm.ErrRecordNotFound) || user.ID == 0 {
			return out, fmt.Errorf("您的信息不存在，请联系管理员")
		}
		// 将密码进行解密
		pwd, err := encrypt.RsaDecode(password, encrypt.DefaultKey)
		if err != nil {
			return out, err
		}
		if !encrypt.EqualsPassword(pwd, user.Password) {
			return out, fmt.Errorf("密码输入错误，请重新输入")
		}
		user.PromoteId = promoteId
		if user.Phone == "" {
			phone = fmt.Sprintf("%s_%s", school, sno) // phone = school+sno
			tokenType = tools.TypeSnoAccount
		} else {
			phone = user.Phone
		}

	} else if typ == "ex" { // 北大、人大、北航、北理登录
		createGetConstute := func(academyName, professionName string) (uint, error) {
			c, _ := new(tables.Constitute).GetInfoByWhere(map[string]interface{}{"name": school, "type": "school"})
			if c.ID == 0 {
				return 0, fmt.Errorf("您的信息不存在，请联系管理员1")
			}
			academy, _ := new(tables.Constitute).GetInfoByWhere(map[string]interface{}{"name": academyName, "type": "academy", "belong_to_id": c.ID})
			if academy.ID == 0 {
				// 创建学院
				if academyName != "" {
					if academy, err = new(tables.Constitute).InsertInfo(tables.Constitute{Name: academyName, Type: "academy", BelongToID: &c.ID}); err != nil {
						log.Logger.ErrorMsgF(ctx, "create Constitute error2:", err.Error())
						return 0, fmt.Errorf("您的信息不存在，请联系管理员2~")
					}
				} else {
					return 0, fmt.Errorf("您的信息不存在，请联系管理员2")
				}
			}
			profession, _ := new(tables.Constitute).GetInfoByWhere(map[string]interface{}{"name": professionName, "type": "profession", "belong_to_id": academy.ID})
			if profession.ID == 0 {
				// 创建专业
				if professionName != "" {
					if profession, err = new(tables.Constitute).InsertInfo(tables.Constitute{Name: professionName, Type: "profession", BelongToID: &academy.ID}); err != nil {
						log.Logger.ErrorMsgF(ctx, "create Constitute error3:", err.Error())
						return 0, fmt.Errorf("您的信息不存在，请联系管理员3~")
					}
				} else {
					return 0, fmt.Errorf("您的信息不存在，请联系管理员3")
				}
			}
			return profession.ID, nil
		}

		err2 := variable.DB.MySQL.Model(&user).Where("school = ? and sno = ?", school, sno).First(&user).Error
		if E.Is(err2, gorm.ErrRecordNotFound) && user.ID == 0 {
			c, _ := new(tables.Constitute).GetInfoByWhere(map[string]interface{}{"name": school, "type": "school"})
			if c.ID == 0 {
				return out, fmt.Errorf("您的信息不存在，请联系管理员1")
			}
			user.AgentID = *c.AgentID
			user.Phone = phone
			user.Type = tables.UserTypeNormal
			user.School = school
			user.Sno = sno
			user.TrailCount = 0 // 游客默认面试次数
			user.PromoteId = promoteId
			// 各个学校走自己的方法获取用户信息
			switch school {
			case "中国人民大学":
				rs := new(RdService)
				s, err := rs.FetchInfo(ctx, sno)
				log.Logger.InfoF(ctx, "FetchInfo -->:", s)
				if err != nil {
					return out, fmt.Errorf("您的信息获取有误，请联系管理员")
				}
				var rdData = BdResp{}
				err = json.Unmarshal([]byte(s), &rdData)
				if err != nil {
					return out, fmt.Errorf("信息获取有误，请联系管理员")
				}
				log.Logger.InfoF(ctx, fmt.Sprintf("rdData -->:%+v", rdData))
				professionId, err := createGetConstute(rdData.Yxsh, rdData.Lqzy)
				if err != nil {
					return out, err
				}
				// 如果数据不存在，则需要补充数据
				user.Name = rdData.Xm
				user.Academy = rdData.Yxsh
				user.Profession = rdData.Lqzy
				user.ConstituteID = professionId
				user.Education = rdData.Xlcc
				user.Grade = rdData.Rxnx
			case "北京航空航天大学", "北京理工大学":
				type tokenExtUser struct {
					Name       string `json:"name"`       // 姓名
					Academy    string `json:"academy"`    // 学院
					Profession string `json:"profession"` // 专业
					Grade      string `json:"grade"`      // 年级
					Education  string `json:"education"`  // 学历
				}
				tokenUser := tokenExtUser{}
				err = json.Unmarshal([]byte(tokenExt), &tokenUser)
				if err != nil {
					return out, fmt.Errorf("信息未解析成功，请联系管理员")
				}
				professionId, err := createGetConstute(tokenUser.Academy, tokenUser.Profession)
				if err != nil {
					return out, err
				}
				user.ConstituteID = professionId

				user.Name = tokenUser.Name
				user.Academy = tokenUser.Academy
				user.Profession = tokenUser.Profession
				user.Education = tokenUser.Education
				user.Grade = tokenUser.Grade
			case "智谱清言":
				// 用户昵称
				timeUnix := time.Now().Unix() % 1670000000
				randCode := fmt.Sprintf("%03v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000))
				nameCode := fmt.Sprintf("用户%s%d", randCode, timeUnix)

				// 如果数据不存在，则需要补充数据
				user.Phone = sno
				user.Sno = sno
				user.School = school
				user.Type = tables.UserTypeAnonymous
				user.AgentID = 75
				user.Name = nameCode
				user.TrailCount = 10000000 // 游客默认面试次数
				user.PromoteId = promoteId
			default:
				return out, fmt.Errorf("您的信息不存在，请联系管理员")
			}
			// 创建学校
			d := variable.DB.MySQL.Create(&user)
			if err := d.Error; err != nil {
				return out, fmt.Errorf(err.Error())
			}
			uid := user.ID
			user.ID = uid
			isExist = false
		}
		if user.Type == tables.UserTypeTeacher {
			return out, fmt.Errorf("仅支持学生使用")
		}
	}
	// 验证是否开启了并发限制，开启后使用特定的盒子登录接口，本接口不能使用
	if user.AgentID != 0 {
		// 获取agent
		agent := &tables.Agent{}
		if d := variable.DB.MySQL.Model(&tables.Agent{}).Where("id = ?", user.AgentID).Find(agent); d.Error != nil {
			return out, err
		}
		// 打开并发数量的进行拦截登录
		if agent.Concurrent > 0 {
			return out, fmt.Errorf("请联系管理员，使用指定环境登录")
		}
	}
	var userAccount = ""
	if user.AccountName != nil {
		userAccount = *user.AccountName
	}
	sto := tools.Sto{Id: user.ID, AgentId: user.AgentID, School: user.School, Type: user.Type, RoleId: user.RoleId, AccountName: userAccount, Phone: user.Phone, Sno: user.Sno, ThemeTopic: tools.ThemeTopicDefault}
	if school == "北京大学" { // 皮肤颜色
		sto.ThemeTopic = tools.ThemeTopicBeida
	}
	// 签发token令牌
	token, err := tools.GenToken(ctx, phone, code, user.ID, variable.Env, tokenType, sto)
	if err != nil {
		return out, fmt.Errorf("系统error,请重试")
	}
	// 4. 将token缓存起来  通过用户id
	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	sc := variable.DB.Redis.Set(redisUserLoginKey, tools.Md5DataString(token), 14*24*time.Hour)
	if sc.Err() != nil {
		return out, fmt.Errorf("用户登录失败，请重试")
	}
	// 设置一下用户信息
	userInfo, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, fmt.Errorf(err.Error())
	}
	out.Id = userInfo.Id
	out.Token = token
	if typ == "sc" {
		out.IsFirstLogin = !user.IsUpdatePwd
	} else {
		out.IsFirstLogin = !isExist
	}
	out.Theme = sto.ThemeTopic
	if !isExist {
		return out, nil
	}
	// 更新用户最后一次登录时间
	variable.DB.MySQL.Model(&user).Where("id = ?", user.ID).Updates(map[string]interface{}{"last_login": time.Now().Format("2006-01-02 15:04:05")}).Limit(1)
	return out, nil
}

func Logout(ctx *gin.Context) (out model.LoginResp, err error) {
	uid, exists := ctx.Get("uid")
	if !exists {
		err = fmt.Errorf("uid not exists")
		return
	}
	aid, exists := ctx.Get("aid")
	if !exists {
		err = fmt.Errorf("aid not exists")
		return
	}
	source, exists := ctx.Get("source")
	if exists {
		if source.(string) == "box" {
			// 删除记录并发信息
			redisConcurrentKey := fmt.Sprintf(common.RedisAgentLoginUsers, aid.(uint))
			uidStr := fmt.Sprintf("%d", uid.(uint))
			variable.DB.Redis.HDel(redisConcurrentKey, uidStr)
		}
		if source.(string) == "third" {
			redisUsersSetKey := fmt.Sprintf(common.RedisAppletsUserSet, aid.(uint))
			variable.DB.Redis.SRem(redisUsersSetKey, uid)
			redisUserKey := fmt.Sprintf(common.RedisAppletsUser, uid.(uint))
			variable.DB.Redis.Del(redisUserKey)
		}
	}

	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, uid.(uint))
	variable.DB.Redis.Del(redisUserLoginKey)
	out.Id = int(uid.(uint))
	return
}

type UserVerifyReq struct {
	Account  string `from:"account" json:"account" validate:"required"`
	Password string `from:"password" json:"password" validate:"required"`
	Type     string `from:"type" json:"type" validate:"oneof=student admin"`
}

func UserVerify(ctx *gin.Context, sc UserVerifyReq) (*tables.User, error) {
	var user = tables.User{}
	aid, exists := ctx.Get("aid")
	if !exists {
		return nil, fmt.Errorf("agent id not exists")
	}
	sno := sc.Account
	password := sc.Password
	if sc.Type == "student" {
		err2 := variable.DB.MySQL.Model(&tables.User{}).Where("agent_id = ? and sno = ?", aid, sno).First(&user)
		if E.Is(err2.Error, gorm.ErrRecordNotFound) || user.ID == 0 {
			return nil, fmt.Errorf("用户信息不存在，请重新输入")
		}
		if !encrypt.EqualsPassword(password, user.Password) {
			return nil, fmt.Errorf("密码输入错误，请重新输入")
		}
	} else {
		err2 := variable.DB.MySQL.Model(&tables.User{}).Where("agent_id = ? and account_name = ?", aid, sno).First(&user)
		if E.Is(err2.Error, gorm.ErrRecordNotFound) || user.ID == 0 {
			return nil, fmt.Errorf("用户信息不存在，请重新输入")
		}
		if tools.Md5DataString(password) != user.Password {
			return nil, fmt.Errorf("密码输入错误，请重新输入")
		}
	}

	// 更新用户最后一次登录时间
	return &user, nil
}

func BoxLogin(ctx *gin.Context, sc model.ScLogin) (out model.LoginResp, err error) {
	var user *tables.User
	var phone = "" // 定义变量
	var code = ""
	var school = ""
	var sno string = ""
	var password = ""
	school = sc.School
	sno = sc.Sno
	password = sc.Password
	if os.Getenv("ENV") != "local" {
		redisCode, err := variable.DB.Redis.Get(fmt.Sprintf(common.RedisCaptchaCodeKey, sc.Time, sc.Rand)).Result()
		if err != nil {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
		if !strings.EqualFold(redisCode, sc.Code) {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
	}
	err2 := variable.DB.MySQL.Model(&user).Where("school = ? and sno = ?", school, sno).First(&user).Error
	if E.Is(err2, gorm.ErrRecordNotFound) || user.ID == 0 {
		return out, fmt.Errorf("您的信息不存在，请联系管理员")
	}
	// 获取agent
	agent := &tables.Agent{}
	if d := variable.DB.MySQL.Model(&tables.Agent{}).Where("id = ?", user.AgentID).Find(agent); d.Error != nil {
		return out, err
	}
	redisConcurrentKey := fmt.Sprintf(common.RedisAgentLoginUsers, agent.ID)
	// 获取所有的登录数量
	allLoginUsers, err := variable.DB.Redis.HGetAll(redisConcurrentKey).Result()
	if err != nil {
		return out, err
	}
	reSave := false
	currentUser := make(map[string]interface{})
	for k, v := range allLoginUsers {
		t, err := time.ParseInLocation(common.DateTimeFormat, v, time.Local)
		if err != nil {
			fmt.Println("time.Parse err:", err)
			continue
		}
		if k == fmt.Sprintf("%d", user.ID) {
			continue
		}
		if time.Now().Before(t.Add(common.BoxExpirationTime)) {
			currentUser[k] = v
		} else {
			reSave = true
		}
	}
	if reSave {
		variable.DB.Redis.HMSet(redisConcurrentKey, currentUser)
	}
	if len(currentUser) >= agent.Concurrent {
		return out, fmt.Errorf("登录数量超过配置总数，请联系管理员")
	}

	// 将密码进行解密
	pwd, err := encrypt.RsaDecode(password, encrypt.DefaultKey)
	if err != nil {
		return out, err
	}
	if !encrypt.EqualsPassword(pwd, user.Password) {
		return out, fmt.Errorf("密码输入错误，请重新输入")
	}
	if user.Phone == "" {
		phone = fmt.Sprintf("%s_%s", school, sno) // phone = school+sno
	} else {
		phone = user.Phone
	}
	var userAccount = ""
	if user.AccountName != nil {
		userAccount = *user.AccountName
	}
	sto := tools.Sto{
		Id:          user.ID,
		AgentId:     user.AgentID,
		School:      user.School,
		Type:        user.Type,
		RoleId:      user.RoleId,
		AccountName: userAccount,
		Phone:       user.Phone,
		Sno:         user.Sno,
		ThemeTopic:  tools.ThemeTopicDefault,
		Source:      "box",
	}

	// 签发token令牌
	token, err := tools.GenToken(ctx, phone, code, user.ID, variable.Env, tools.TypeSnoAccount, sto)
	if err != nil {
		return out, fmt.Errorf("系统error,请重试")
	}
	// 4. 将token缓存起来  通过用户id
	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	err = variable.DB.Redis.Set(redisUserLoginKey, tools.Md5DataString(token), 14*24*time.Hour).Err()
	if err != nil {
		return out, fmt.Errorf("用户登录失败，请重试")
	}
	// 设置一下用户信息
	userInfo, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, fmt.Errorf(err.Error())
	}
	out.Id = userInfo.Id
	out.Token = token
	out.IsFirstLogin = !user.IsUpdatePwd
	out.Theme = sto.ThemeTopic
	// 记录登录的数量
	currentTime := time.Now().Format(common.DateTimeFormat)
	uidStr := fmt.Sprintf("%d", user.ID)
	err = variable.DB.Redis.HSet(redisConcurrentKey, uidStr, currentTime).Err()
	if err != nil {
		return out, err
	}
	// 更新用户最后一次登录时间
	variable.DB.MySQL.Model(&user).Where("id = ?", user.ID).Updates(map[string]interface{}{"last_login": time.Now().Format("2006-01-02 15:04:05")}).Limit(1)
	return out, nil
}

// ThirdLogin 第三方登录，改登录无法使用面试(获取不到面试题目)
func ThirdLogin(ctx *gin.Context, sc model.ScLogin) (out model.LoginResp, err error) {
	var user *tables.User
	var phone = "" // 定义变量
	var code = ""
	var school = ""
	var sno string = ""
	var password = ""
	school = sc.School
	sno = sc.Sno
	password = sc.Password
	if os.Getenv("ENV") != "local" {
		redisCode, err := variable.DB.Redis.Get(fmt.Sprintf(common.RedisCaptchaCodeKey, sc.Time, sc.Rand)).Result()
		if err != nil {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
		if !strings.EqualFold(redisCode, sc.Code) {
			return out, fmt.Errorf("验证码已过期,请重新获取")
		}
	}
	err2 := variable.DB.MySQL.Model(&user).Where("school = ? and sno = ?", school, sno).Preload("Agent").First(&user).Error
	if E.Is(err2, gorm.ErrRecordNotFound) || user.ID == 0 {
		return out, fmt.Errorf("您的信息不存在，请联系管理员")
	}
	// 将密码进行解密
	pwd, err := encrypt.RsaDecode(password, encrypt.DefaultKey)
	if err != nil {
		return out, err
	}
	if !encrypt.EqualsPassword(pwd, user.Password) {
		return out, fmt.Errorf("密码输入错误，请重新输入")
	}
	if user.Phone == "" {
		phone = fmt.Sprintf("%s_%s", school, sno) // phone = school+sno

	} else {
		phone = user.Phone
	}
	var userAccount = ""
	if user.AccountName != nil {
		userAccount = *user.AccountName
	}
	sto := tools.Sto{
		Id:          user.ID,
		AgentId:     user.AgentID,
		School:      user.School,
		Type:        user.Type,
		RoleId:      user.RoleId,
		AccountName: userAccount,
		Phone:       user.Phone,
		Sno:         user.Sno,
		ThemeTopic:  tools.ThemeTopicDefault,
		Source:      "third",
	}

	// 签发token令牌
	token, err := tools.GenToken(ctx, phone, code, user.ID, variable.Env, tools.TypeSnoAccount, sto)
	if err != nil {
		return out, fmt.Errorf("系统error,请重试")
	}
	// 4. 将token缓存起来  通过用户id
	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	err = variable.DB.Redis.Set(redisUserLoginKey, tools.Md5DataString(token), 14*24*time.Hour).Err()
	if err != nil {
		return out, fmt.Errorf("用户登录失败，请重试")
	}
	// 设置一下用户信息
	userInfo, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, fmt.Errorf(err.Error())
	}
	out.Id = userInfo.Id
	out.Token = token
	out.IsFirstLogin = !user.IsUpdatePwd
	out.Theme = sto.ThemeTopic
	if user.UseAppletsEndTime.After(time.Now()) {
		out.CanUseApplets = true
	}

	// 更新用户最后一次登录时间
	variable.DB.MySQL.Model(&user).Where("id = ?", user.ID).Updates(map[string]interface{}{"last_login": time.Now().Format("2006-01-02 15:04:05")}).Limit(1)
	return out, nil
}

// 补全用户信息
func UserInfo(ctx *gin.Context, userInfo model.UserInfoRequest, uid int) *errors.ErrRes {
	var user tables.User
	user.Name = userInfo.Name
	user.Profession = userInfo.Profession
	user.School = userInfo.School
	user.Grade = userInfo.Grade
	if result := variable.DB.MySQL.Where("id = ?", uid).Updates(&user).Limit(1); result.Error != nil {
		return errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf(errors.DatabaseMySqlOptionFail))
	}
	return nil
}

// 用户名登录,后台登录
func UserLogin(ctx *gin.Context, sc model.SendCodeRequest) (out model.AdminLoginResp, err error) {
	var username = sc.UserName
	var password = sc.Password
	var action = sc.Action
	var pcToken = sc.Token
	var clientId = sc.ClientId
	var user = tables.User{}
	if action == "token_login" {
		if len(pcToken) == 0 && len(clientId) == 0 {
			return out, fmt.Errorf("登录token不能为空")
		}
		// pcToken, _ := url.QueryUnescape(pcToken)
		// 解析token
		pcToken, _ = encrypt.RsaDecode(pcToken, encrypt.DefaultKey)
		tokenArr := strings.Split(pcToken, "-")
		if len(tokenArr) == 0 {
			return out, fmt.Errorf("token解析失败，请重试")
		}
		dateTime, _ := strconv.ParseInt(tokenArr[3], 10, 64)
		isTimeout := time.Now().UnixMilli() - dateTime
		if isTimeout < 0 || isTimeout > 120000 { // 两分钟过期
			return out, fmt.Errorf("您登录超时，请重试")
		}
		sign := tools.Md5DataString(fmt.Sprintf("%s-%s-%d", variable.Config.Ex.Bd.ClientId, variable.Config.Ex.Bd.AppSecret, dateTime))
		if sign != tokenArr[2] {
			return out, fmt.Errorf("您的token校验失败，请重试")
		}
		school := tokenArr[5]
		email := tokenArr[4]
		// 查询用户是否存在
		d := variable.DB.MySQL.Where("email = ? and type = ? and school = ?", email, tables.UserTypeAdmin, school).Find(&user)
		if d.Error == gorm.ErrRecordNotFound {
			return out, fmt.Errorf("您目前没有管理员权限，请联系管理员")
		}
		if d.Error != nil {
			return out, fmt.Errorf("您目前没有管理员权限，请联系管理员")
		}

	} else {
		if username == "" || password == "" {
			return out, fmt.Errorf("用户名或密码不能为空~")
		}
		// 1. 根据用户名获取用户信息
		user, err = tables.GetAgentInfoByUserName(username)
		if err != nil {
			return out, err
		}
		if password != user.Password {
			return out, fmt.Errorf("密码输入错误，请重新输入")
		}
	}
	if user.Level == 0 {
		return out, fmt.Errorf("您无权限登录~")
	}
	if user.Name == "" {
		return out, fmt.Errorf("用户不存在~")
	}

	// 2. 比较用户名和密码
	// pwd, err := encrypt.RsaDecode(password)
	// if err != nil {
	// 	return out, fmt.Errorf("密码解析错误，请重新提交")
	// }
	// if !encrypt.EqualsPassword(user.Password, pwd) {
	// 	return out, fmt.Errorf("密码输入错误，请重新输入")
	// }

	var phone = user.Phone
	// 3. 判断该用户是否过期
	//scr := variable.DB.Redis.Get(fmt.Sprintf(common.RedisAgentIsTimeoutKey, user.AgentID))
	//str, err := scr.Result()
	//if err != nil {
	//	return out, fmt.Errorf("您的服务未生效，请联系管理员")
	//}
	//endStamp, _ := strconv.ParseInt(str, 10, 64)
	//
	//if endStamp-time.Now().Unix() < common.AgentTimeKey {
	//	return out, fmt.Errorf("您的服务已到期")
	//}

	user.Password = password
	var userAccount = ""
	if user.AccountName != nil {
		userAccount = *user.AccountName
	}
	sto := tools.Sto{Id: user.ID, AgentId: user.AgentID, School: user.School, Type: user.Type, RoleId: user.RoleId, AccountName: userAccount, Phone: user.Phone, Sno: user.Sno, ThemeTopic: tools.ThemeTopicDefault}
	if user.School == "北京大学" {
		sto.ThemeTopic = tools.ThemeTopicBeida
	}
	// 4. 签发token令牌
	token, err := tools.GenToken(ctx, phone, password, user.ID, variable.Env, tools.TypeAccount, sto)
	if err != nil {
		return out, fmt.Errorf(err.Error())
	}

	// 5. 将token缓存起来
	redisAccountKey := fmt.Sprintf(common.RedisAccountKey, *user.AccountName)
	scd := variable.DB.Redis.Set(redisAccountKey, tools.Md5DataString(token), 14*24*time.Hour)
	if scd.Err() != nil {
		return out, fmt.Errorf("用户登录失败，请重试")
	}
	out.Id = int(user.ID)
	out.Name = *user.AccountName
	out.School = user.School
	out.Profession = user.Profession
	out.Grade = user.Grade
	out.Education = user.Education
	out.Type = user.Type
	out.Phone = user.Phone
	out.Sno = user.Sno
	out.Token = token
	out.Academy = user.Academy
	out.Level = user.Level
	out.Mail = user.Email
	out.Theme = sto.ThemeTopic
	return out, nil

}

func BindingSync(ctx *gin.Context, bp model.BindingSync) (out interface{}, err error) {
	uid, exists := ctx.Get("uid")
	if !exists {
		return out, fmt.Errorf("用户信息不存在")
	}
	user, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return out, fmt.Errorf("用户不存在")
	}
	if err = variable.DB.MySQL.Model(&user).Update("is_sync", bp.IsSync).Error; err != nil {
		return
	}
	variable.DB.Redis.Del(fmt.Sprintf(common.RedisUserInfoKey, uid.(uint)))
	return out, nil
}

// 绑定手机号，返回值需要和登录接口保持一致
func BindingPhone(ctx *gin.Context, bp model.BindingPhone) (out interface{}, err error) {
	uid, exists := ctx.Get("uid")
	if !exists {
		return out, fmt.Errorf("用户信息不存在")
	}
	// 首先验证手机号
	phone := bp.Phone
	if !tools.CheckMobile(phone) {
		return out, fmt.Errorf(errors.PhoneCheckError)
	}
	user, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return out, fmt.Errorf("用户不存在")
	}
	user.Phone = phone
	// 判断该手机号是否被绑定
	_, exists, _ = tables.GetUserInfoByPhone(phone)
	if exists {
		return out, fmt.Errorf("手机号已经被绑定")
	}

	// 更新手机号
	_, err = tables.UpdateUserInfoById(user, int(uid.(uint)), []string{"phone"})
	if err != nil {
		return out, fmt.Errorf("手机号绑定失败,请重试")
	}

	// 签发token令牌
	code := fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(10000))
	token, err := tools.GenToken(ctx, phone, code, user.ID, variable.Env, tools.TypePhone, user)
	if err != nil {
		return out, fmt.Errorf(err.Error())
	}
	// 更新token
	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	sc := variable.DB.Redis.Set(redisUserLoginKey, tools.Md5DataString(token), 14*24*time.Hour)
	if sc.Err() != nil {
		return out, fmt.Errorf("用户登录失败，请重试")
	}
	_, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, fmt.Errorf(err.Error())
	}

	return out, nil
}

// 绑定邮箱，返回值需要和登录接口保持一致
func BindingMail(ctx *gin.Context, bp model.BindingMail) (out interface{}, err error) {
	uid, exists := ctx.Get("uid")
	if !exists {
		return out, fmt.Errorf("用户信息不存在")
	}
	mail := bp.Mail
	code := bp.Code
	redisCodeKey := fmt.Sprintf(common.RedisCodeKey, tools.Md5DataString(mail))
	sc := variable.DB.Redis.Get(redisCodeKey)
	if sc.Err() != nil {
		return out, fmt.Errorf("操作失败, 请重新操作")
	}
	s, _ := sc.Result()
	if s != code {
		return out, fmt.Errorf("验证码错误，请重新输入")
	}

	user, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return out, fmt.Errorf("用户不存在")
	}
	user.Email = mail
	// 更新邮箱
	_, err = tables.UpdateUserInfoById(user, int(uid.(uint)), []string{"email"})
	if err != nil {
		return out, fmt.Errorf("邮箱绑定失败,请重试")
	}

	_, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, er.Err
	}

	return out, nil
}

// 邮箱找回密码，确认密码
func ConfirmCode(ctx *gin.Context, bp model.BindingMail) error {
	mail := bp.Mail
	code := bp.Code
	user, err := tables.GetUserInfoByUserEmail(mail, []string{tables.UserTypeAnonymous, tables.UserTypeNormal})
	if err != nil || user.ID == 0 {
		return fmt.Errorf("用户不存在")
	}

	redisCodeKey := fmt.Sprintf(common.RedisCodeMailFindKey, tools.Md5DataString(mail))
	sc := variable.DB.Redis.Get(redisCodeKey)
	if sc.Err() != nil {
		return fmt.Errorf("验证码错误, 请重新操作")
	}
	s, _ := sc.Result()
	if s != code {
		return fmt.Errorf("验证码错误, 请重新输入")
	}
	return nil
}

// 解绑绑定发送验证码
func DelMail(ctx *gin.Context) (out interface{}, err error) {
	// 发送邮箱
	uid, exists := ctx.Get("uid")
	if !exists {
		return out, fmt.Errorf("用户不存在")
	}

	user, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return out, fmt.Errorf("用户不存在")
	}
	user.Email = ""
	// 更新邮箱
	_, err = tables.UpdateUserInfoById(user, int(uid.(uint)), []string{"email"})
	if err != nil {
		return out, fmt.Errorf("邮箱解除绑定失败")
	}
	_, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return out, fmt.Errorf(err.Error())
	}
	return out, nil
}

// 修改密码
func ResetPwdFind(ctx *gin.Context, pr model.PwdResetFind) *errors.ErrRes {
	mail := pr.Mail
	user, err := tables.GetUserInfoByUserEmail(mail, []string{tables.UserTypeAnonymous, tables.UserTypeNormal})
	if err != nil || user.ID == 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户不存在"))
	}
	newPwd, _ := encrypt.RsaDecode(pr.NewPwd, encrypt.DefaultKey)
	confirmPwd, _ := encrypt.RsaDecode(pr.ConfirmPwd, encrypt.DefaultKey)
	if newPwd != confirmPwd {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("两次输入密码不一致"))
	}
	// 将密码解析出来 + 重新加密
	pwd, _ := encrypt.RsaDecode(pr.NewPwd, encrypt.DefaultKey)
	password, _ := encrypt.EncryptPassword(pwd)
	user.Password = password
	// 更新密码
	_, err = tables.UpdateUserInfoById(user, int(user.ID), []string{"password"})
	if err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("更新密码失败"))
	}
	// 清除token重新登录
	redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	variable.DB.Redis.Del(redisUserLoginKey)
	_, er := SetUserInfo(ctx, user.ID)
	if er != nil {
		return er
	}

	return nil
}

// 重置密码
func ResetPwd(ctx *gin.Context, pr model.PwdReset) *errors.ErrRes {
	uid, exists := ctx.Get("uid")
	if !exists {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户不存在"))
	}

	newPwd, _ := encrypt.RsaDecode(pr.NewPwd, encrypt.DefaultKey)
	confirmPwd, _ := encrypt.RsaDecode(pr.ConfirmPwd, encrypt.DefaultKey)
	if newPwd != confirmPwd {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("两次输入密码不一致"))
	}
	// 更新用户密码
	user, err := tables.GetUserInfoById(int(uid.(uint)))
	// 判断用户身份
	if user.Type == tables.UserTypeAnonymous || user.Sno == "" || len(user.Sno) == 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("您的身份不需要修改密码"))
	}
	if err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户不存在"))
	}
	// 将密码解析出来 + 重新加密
	pwd, _ := encrypt.RsaDecode(pr.NewPwd, encrypt.DefaultKey)
	password, _ := encrypt.EncryptPassword(pwd)
	user.Password = password
	user.IsUpdatePwd = true
	// 更新密码
	_, err = tables.UpdateUserInfoById(user, int(uid.(uint)), []string{"password", "is_update_pwd"})
	if err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("更新密码"))
	}
	// 清楚tokentoken重新登录
	// redisUserLoginKey := fmt.Sprintf(common.RedisUserLoginKey, user.ID)
	// variable.DB.Redis.Del(redisUserLoginKey)
	// _, er := SetUserInfo(ctx, user.ID)
	// if er != nil {
	// 	return er
	// }

	return nil
}

// 获取当前用户的信息
func GetCurrentUserInfo(ctx *gin.Context) (model.UserInfo, *errors.ErrRes) {
	var out model.UserInfo
	uid, exists := ctx.Get("uid")
	if !exists {
		return out, errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户不存在"))
	}
	//// 获取用户信息
	//sc := variable.DB.Redis.Get(fmt.Sprintf(common.RedisUserInfoKey, uid.(uint)))
	//s, err := sc.Result()
	//if err != nil || s == "" {
	out, _ = SetUserInfo(ctx, uid.(uint)) // 设置当前用户信息
	//return out, nil
	//} else {
	//	err = json.Unmarshal([]byte(s), &out)
	//	if err != nil {
	//		return out, errors.NewErrInfo(30003, err)
	//	}
	//}
	if out.UM != nil && out.UM.MemberExpire != "" {
		daySum, _ := tools.GetDaysBetween2Date("2006-01-02 15:04:05", out.UM.MemberExpire, time.Now().UTC().Format("2006-01-02 15:04:05"))
		out.UM.SurplusTime = daySum
	}

	// 查询用户是否已经关闭相关面试报告更新的信息
	intUid := int(uid.(uint))
	reportMs, err := new(tables.Session).FindReportsByUID(intUid)
	if err != nil {
		return out, errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf(errors.DatabaseMySqlOptionFail))
	}
	result, err := variable.DB.Redis.Get(fmt.Sprintf(common.RedisNewReportIconKey, intUid)).Result()
	if err == nil && result != "" {
		out.CloseIcon = true
	}
	result, err = variable.DB.Redis.Get(fmt.Sprintf(common.RedisNewReportBannerKey, intUid)).Result()
	if err == nil && result != "" {
		out.CloseBanner = true
	} else if err == redis.Nil {
		// 查询用户的面试报告记录, 如果没有面试记录不提醒该banner
		if len(reportMs) == 0 {
			out.CloseBanner = true
			go func() {
				variable.DB.Redis.Set(fmt.Sprintf(common.RedisNewReportBannerKey, intUid), "1", time.Hour*24*45)
			}()
		}
	}
	result, err = variable.DB.Redis.Get(fmt.Sprintf(common.RedisNewReportEntranceKey, intUid)).Result()
	if err == nil && result != "" {
		out.OpenReport = true
	} else if err == redis.Nil {
		// 查询用户的面试报告记录, 如果没有面试记录不提醒该红点
		if len(reportMs) == 0 {
			out.OpenReport = true
			go func() {
				variable.DB.Redis.Set(fmt.Sprintf(common.RedisNewReportEntranceKey, intUid), "1", time.Hour*24*45)
			}()
		}
	}

	return out, nil
}

// 设置用户信息
func SetUserInfo(ctx *gin.Context, uid uint) (model.UserInfo, *errors.ErrRes) {
	var out model.UserInfo
	if uid == 0 {
		return out, errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户不存在"))
	}

	// 获取用户信息
	user, err := tables.GetAgentInfoByUid(uid)
	if err != nil {
		return out, errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("该用户不存在"))
	}
	if user.IconUrl != "" {
		user.IconUrl = variable.Config.UfsInfo.UfsUrl + variable.Config.UfsInfo.BucketPrefix + user.IconUrl
	}
	out.Id = int(user.ID)
	out.Sno = user.Sno
	out.Name = user.Name
	out.School = user.School
	out.Academy = user.Academy
	out.Grade = user.Grade
	out.Education = user.Education
	out.Profession = user.Profession
	out.Phone = user.Phone
	out.Mail = user.Email
	out.Type = user.Type
	out.IconUrl = user.IconUrl
	out.Level = user.Level
	out.TrailCount = user.TrailCount
	out.IsSync = user.IsSync
	out.AgentIsSync = user.Agent.IsSync
	if user.UseAppletsEndTime.After(time.Now()) {
		out.CanUseApplets = true
	}
	out.UM = &model.UserMember{}
	if user.IsMember != tables.MemberStatusDefault { // 会员生效
		// 获取用户的会员状态
		m, _ := new(tables.Member).GetMemeberByUid(int64(uid))
		// 组装数据
		var memeberInfo model.UserMember
		if m.ID != 0 {
			// 计算过期时间
			currentTime := time.Now().UTC().Format("2006-01-02 15:04:05")
			daySum, _ := tools.GetDaysBetween2Date("2006-01-02 15:04:05", m.MemberExpire, currentTime)
			memeberInfo.MemberExpire = m.MemberExpire
			memeberInfo.MemberLevel = 0
			memeberInfo.MemeberStartTime = m.MemberStartTime
			memeberInfo.MemberStatus = true
			memeberInfo.SurplusTime = daySum
			memeberInfo.MemeberTitle = m.Title
			if daySum+1 <= 0 {
				memeberInfo.MemberStatus = false
				memeberInfo.SurplusTime = 0
				// 该会员已经过期
				log.Logger.InfoF(ctx, fmt.Sprintf("用户%d的会员已经过期", user.ID))
			}
		}
		out.UM = &memeberInfo
	}
	b, _ := json.Marshal(out)
	variable.DB.Redis.Set(fmt.Sprintf(common.RedisUserInfoKey, uid), string(b), 30*24*time.Hour)
	return out, nil
}

// 更新用户的会员状态
func MemberStatus() {
	userList, _ := new(tables.User).GetUsersByWhereStr(fmt.Sprintf("type = \"%s\" and is_member = %d", tables.UserTypeAnonymous, tables.MemberStatusHas), "*")
	if len(userList) == 0 {
		return
	}
	for _, u := range userList {
		var uid = u.ID
		m, _ := new(tables.Member).GetMemeberByUid(int64(uid))
		duestr := m.MemberExpire
		duetimecst, _ := time.ParseInLocation("2006-01-02 15:04:05", duestr, time.Local)
		sdt := time.Now().UTC().Local()
		if duetimecst.After(sdt) {
			continue
		}
		// 更新user表
		u.IsMember = tables.MemberStatusExpire
		tables.UpdateUserInfoById(u, int(u.ID), []string{"is_member"})
	}
}

// 是否需要弹窗
func GetNotificationTopicNew(ctx *gin.Context) (bool, error) {
	uid, exists := ctx.Get("uid")
	if !exists {
		return false, fmt.Errorf("用户信息错误，需要重新登录") // 用户不存在，不需要弹窗
	}
	// 先查询redis判断是否开启查询
	s, err := redisop.GetStr(ctx, common.RedisTopicNewNotify)
	if err != nil {
		return false, nil // 不需要弹窗
	}
	if s != "1" {
		return false, nil // 不需要弹窗
	}
	// 在查询该用户是否已经被弹窗
	s, err = redisop.GetStr(ctx, fmt.Sprintf(common.RedisTopicNewUid, uid.(uint)))
	if err != nil {
		// 查询数据库
		return false, nil
	}
	if s != "" {
		return false, nil // 不需要弹窗
	}
	// 设置用户不在弹窗
	err = redisop.SetStr(ctx, fmt.Sprintf(common.RedisTopicNewUid, uid.(uint)), "false", -1) // 设置永久不在弹窗
	if err != nil {
		return true, nil
	}
	return true, nil
}

func GetDefaultUserTrailCount(ctx *gin.Context) int {
	// 查一下redis是否设置
	s, err := redisop.GetStr(ctx, common.RedisDefaultTrailCount)
	if err != nil {
		return common.DefaultUserTrailCount
	}
	if s == "" {
		return common.DefaultUserTrailCount
	}
	i, err := strconv.Atoi(s)
	if err != nil {
		return common.DefaultUserTrailCount
	}
	return i
}

func SetDefaultUserTrailCount(ctx *gin.Context, trailCount int) error {
	err := redisop.SetStr(ctx, common.RedisDefaultTrailCount, strconv.Itoa(trailCount), -1)
	if err != nil {
		return err
	}
	return nil
}

// 添加企业信息
func AddEnterprise(ctx *gin.Context, enterprise model.EnterpriseInfo) (model.EnterpriseKey, error) {
	var ek model.EnterpriseKey
	// 添加用户信息
	name := enterprise.Name
	prefix := enterprise.Prefix
	u, err := new(tables.User).GetUsersByWhereStr("id = 1", "*")
	if err != nil {
		return ek, fmt.Errorf("user add is error~")
	}
	if len(u) == 0 {
		return ek, fmt.Errorf("user add is error~")
	}
	*u[0].AccountName = fmt.Sprintf("%s_t", prefix)
	u[0].Password = ""
	u[0].Name = name
	u[0].ID = 0
	d := variable.DB.MySQL.Create(&u[0])
	if err := d.Error; err != nil {
		return ek, fmt.Errorf(err.Error())
	}
	// 添加数据,
	appKey := tools.RandStringLowBytes(18)
	appSecret := tools.RandStringDigitBytes(27)
	cc := tables.CommonConf{}
	cc.ConfValue = fmt.Sprintf("%s_%s", prefix, appKey)
	cc.ExtendInfo = fmt.Sprintf("app_%s", appSecret)
	cc.Remarks = strconv.Itoa(int(u[0].ID))
	cc.Other = prefix
	cc.Type = 1
	cc.ConfKey = "token"
	d2 := variable.DB.MySQL.Create(&cc)
	if err := d2.Error; err != nil {
		return ek, fmt.Errorf(err.Error())
	}
	ek.AppKey = cc.ConfValue
	ek.AppSecret = cc.ExtendInfo
	return ek, nil
}

func VerifyToken(ctx *gin.Context, req model.TokenVerifyReset) (timeStep string, err error) {
	encryptKey := encrypt.DefaultKey
	cids := map[string][]string{
		//variable.Config.Ex.Bd.ClientId: {variable.Config.Ex.Bd.GrantType, encrypt.BdKey, variable.Config.Ex.Bd.AppSecret},
		//variable.Config.Ex.Bh.ClientId: {variable.Config.Ex.Bh.GrantType, encrypt.BhKey, variable.Config.Ex.Bh.AppSecret},
		//variable.Config.Ex.Bl.ClientId: {variable.Config.Ex.Bl.GrantType, encrypt.BlKey, variable.Config.Ex.Bl.AppSecret},
		//variable.Config.Ex.Rd.ClientId: {variable.Config.Ex.Rd.GrantType, encrypt.RdKey, variable.Config.Ex.Rd.AppSecret},
	}
	s, ok := cids[req.ClientId]
	if !ok {
		return "", fmt.Errorf("client_id 不合法")
	}
	encryptKey = s[1]
	// 解析token
	token, _ := encrypt.RsaDecode(req.Token, encryptKey)
	tokenArr := strings.Split(token, "-")
	if len(tokenArr) < 6 {
		return "", fmt.Errorf("token解析失败，请重试")
	}
	timeStr := tokenArr[3]
	if len(timeStr) > 10 {
		timeStr = timeStr[:10]
	}
	dateTime, _ := strconv.ParseInt(timeStr, 10, 64)
	if time.Now().Unix()-dateTime > 120 { // 两分钟过期
		return "", fmt.Errorf("您登录超时，请重试")
	}
	sign := tools.Md5DataString(fmt.Sprintf("%s-%s-%s", req.ClientId, cids[req.ClientId][2], tokenArr[3]))
	if sign != tokenArr[2] {
		return "", fmt.Errorf("您的token校验失败，请重试")
	}
	return timeStr, nil
}

// 获取所有代理商的过期时间
func SendAgentExp() {
}
